﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SableCC15
{
    /// <summary>
    /// Custom SymbolTable Exception
    /// </summary>
    public class SymbolTableException : Exception
    {
        public SymbolTableException(string str)
            : this(str, null)
        { }
        public SymbolTableException(string str, Exception InnerException)
            : base(str, InnerException)
        { }
    }

    /// <summary>
    /// Custom Symboltable there should only exist one! therefore singleton!
    /// </summary>
    public class SymbolTable
    {
        //HACK made public for testing
        private Dictionary<string, SymbolTableEntry> _symbols = new Dictionary<string, SymbolTableEntry>();

        public Dictionary<string, SymbolTableEntry> Symbols { get { return _symbols; } }
        /// <summary>
        /// The private instance to ensure singleton pattern
        /// </summary>
        private static SymbolTable _instance;

        /// <summary>
        /// Private constructor to prevent to initializing new instances.
        /// </summary>
        private SymbolTable()
        { }

        /// <summary>
        /// Gets the instance of the symboltable.
        /// </summary>
        /// <returns></returns>
        public static SymbolTable GetInstance()
        {
            if (_instance == null)
            {
                _instance = new SymbolTable();
                return _instance;
            }
            return _instance;
        }

        /// <summary>
        /// Adds new symbols to the symboltable
        /// </summary>
        public void Add(SymbolTableEntry entry)
        {
            if (entry == null)
                throw new SymbolTableException("The entry you're trying to add is null!");

            if (entry is VariableEntry)
            {
                VariableEntry tmpent = entry as VariableEntry;
                if (!_symbols.ContainsKey(tmpent.Id + "," + tmpent.Scope))
                    _symbols.Add(tmpent.Id + "," + tmpent.Scope, entry);
                else
                    throw new SymbolTableException(string.Format("The Variable {0} already exists inside scope {1}", tmpent.Id, tmpent.Scope));

                return;
            }

            //if(entry is something_ELSE)
            //{

            //}

            /*
                 
             if(entry is somethin_else)
             * {
             * }
             */
        }

        /// <summary>
        /// Removes the entry from SymbolTable
        /// </summary>
        /// <param name="entry"></param>
        public void Remove(SymbolTableEntry entry)
        {
            if (entry == null)
                throw new SymbolTableException("The entry you're trying to add is null!");

            //The chain of if-elses
            if (entry is VariableEntry)
            {
                VariableEntry tempentry = entry as VariableEntry;
                _symbols.Remove(tempentry.Id + "," + tempentry.Scope);
            }
        }

        /// <summary>
        /// Checks if the requested variable exists in the symboltable
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        public bool VariableExists(string varName, string scope)
        {
            return _symbols.ContainsKey(varName + "," + scope);
        }

        /// <summary>
        /// Checks if the requested variable exists in the symboltable
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="scope"></param>
        /// <param name="scopelevel"></param>
        /// <returns></returns>
        public bool VariableExists(string varName, string scope, int scopelevel)
        {
            bool variableExists = _symbols.ContainsKey(varName + "," + scope);
            bool scopeLevelCorrect = false;
            VariableEntry entry = null;
            if (variableExists)
            {
                entry = _symbols[varName + "," + scope] as VariableEntry;
                scopeLevelCorrect = entry.ScopeLevel <= scopelevel; //Satte curly brackets om, da det dette smed med expections når if ikke var sand fordi så var entry stadigt null
            }

            return variableExists && scopeLevelCorrect;
        }

        /// <summary>
        /// Finds and returns the requested entry!
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        public SymbolTableEntry Find(string varName, string scope)
        {
            if (!VariableExists(varName, scope))
                throw new SymbolTableException("The Entry does not exist!");

            return _symbols[varName + "," + scope];
        }

        /// <summary>
        /// Finds and returns the requested entry!
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="scope"></param>
        /// <param name="scopelevel"></param>
        /// <returns></returns>
        public SymbolTableEntry Find(string varName, string scope, int scopelevel)
        {
            if (!VariableExists(varName, scope, scopelevel))
                throw new SymbolTableException(varName + " does not exist in the current context,");

            return _symbols[varName + "," + scope];
        }

        /// <summary>
        /// Removes every variables within the given scopelevel
        /// </summary>
        /// <param name="scopelevel"></param>
        /// <returns></returns>
        public bool RemoveVariableFromScopeLevel(int scopelevel)
        {
            List<SymbolTableEntry> values = _symbols.Values.Where(i => (i as VariableEntry).ScopeLevel == scopelevel).ToList();
            for (int i = 0; i < values.Count(); i++)//prøvede at ændre på listen der gennemgås (values) inde i en foreach så går c# psykisk ned
            {
                VariableEntry ve = (VariableEntry)values[i]; //Bliver nød til at caste da SymbolTableEntry ikke har adgang til de fields
                _symbols.Remove(ve.Id + "," + ve.Scope);
            }

            return values.Count() == 0;
        }
    }
}
